/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief MottiMission parser component types.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * This module contains container types for different elements found
 * from MottiMission tree. Each element is a standalone object and is
 * capable of generating its contents in XML.
 *
 */
#ifndef _MACI_INTERFACES_TASKCTRL_MOTTI_MISSION_TYPES_HPP_
#define _MACI_INTERFACES_TASKCTRL_MOTTI_MISSION_TYPES_HPP_

#include <math.h>
#include <list>
#include <string>
#include "gimutils.h"
#include <sstream>


/** Motti-project specific tools.
 *
 * Motti namespace contains all classes and functions related to the
 * Motti project.
 */
namespace Motti {

  /** Base structure for all Motti Mission elements. 
   *
   * Contains common features like Print and ToXMLString() pure
   * virtual functions.
   */
  struct SBaseElement
  {
    SBaseElement() {}
    virtual ~SBaseElement() {}

    virtual void Print(const unsigned int &aLevel, const std::string &aPrefix="") = 0;
    virtual std::string ToXMLString(void) const = 0;
    virtual bool Validate(void) const { return true; }
    virtual void Clear(void) = 0;
  };

  
  /** Node header.
   * 
   * This element contains PROPERTY information about a node. When a
   * node is parsed for this informatino, it should contains
   * properties with names 'id', 'rule' and 'trigger'.
   *
   * SNodeHeader::ToXMLString() implementation produces the internal
   * properties expressed as XML property list. No tree structure is
   * generated for NodeHeader.
   */
  struct SNodeHeader : public SBaseElement
  {
    SNodeHeader(const std::string &aId = "",
                const std::string &aRule = "",
                const std::string &aTrigger = "")
      : id(aId),
        rule(aRule),
        trigger(aTrigger)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      return "ID=\"" + id + "\" rule=\"" + rule + "\" trigger=\"" + trigger + "\"";
    }
    void Clear(void) {
      id.clear();
      rule.clear();
      trigger.clear();
    }

    // Objects.
    std::string id;
    std::string rule;
    std::string trigger;
  };



  /** MissionLocation tag.
   *
   * This node contains MissionLocation information. MissionLocation node is
   * identified by 'MissionLocation' name, and may or may not contain a
   * nodeheader field. (If nodeheader field is not present, the ID tag
   * of that container is empty) Other information contains X and Y
   * coordinate, as well as optional Slot.  If slot is negative, it is
   * assumed to be absent and will not be exported when encoding to
   * XML.
   *
   */
  struct SMissionLocation : public SBaseElement
  {
    SMissionLocation(const SNodeHeader &aNodeHeader = SNodeHeader(),
              const float &aX = 0.00, 
              const float &aY = 0.00,
              const int &aSlot = -1)
      : nodeheader(aNodeHeader),
        x(aX),
        y(aY),
        slot(aSlot)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      // Print nodeheader only if id is not empty.
      if (!nodeheader.id.empty()) 
        r << "<MissionLocation " << nodeheader.ToXMLString() << ">" << std::endl;
      else 
        r << "<MissionLocation>" << std::endl;

      r << "<x>" << x << "</x>" << std::endl;
      r << "<y>" << y << "</y>" << std::endl;

      // Print slot if value > 0
      if (slot >= 0) 
        r << "<slot>" << slot << "</slot>" << std::endl;

      r << "</MissionLocation>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      nodeheader.Clear();
      x = 0.00;
      y = 0.00;
      slot = -1;
    }


    SNodeHeader nodeheader;
    float x;
    float y;
    int slot;
  };
  typedef std::list<SMissionLocation> TMissionLocationArray;

  
  /** Mission general information.
   * 
   * This tag contains mission general information, like mission name,
   * description and departure time. Header also contains the target
   * platform at which pallets belonging to this mission should be
   * delivered to.
   */
  struct SMissionHeader : public SBaseElement
  {
    SMissionHeader(const std::string &aName = "",
                   const std::string &aDescription = "",
                   const gim::time &aDeparture_time = gim::time(),
                   const SMissionLocation &aMissionLocation = SMissionLocation(),
                   const unsigned int aTruck_Id = 0) 
      : name(aName),
        description(aDescription),
        departure_time(aDeparture_time),
        MissionLocation(aMissionLocation),
        truck_id(aTruck_Id)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<header>" << std::endl;
      r << "<name>" << name << "</name>" << std::endl;
      r << "<description>" << description << "</description>" << std::endl;
      r << "<time>" << departure_time.getDateString() << "</time>" << std::endl;
      r << "<ctime>" << static_cast<unsigned int>( floor( departure_time.getTimeInSeconds() )) << "</ctime>" << std::endl;
      r << "<platform>" << std::endl;
      r << MissionLocation.ToXMLString();
      r << "</platform>" << std::endl;
      r << "<truckid>" << truck_id << "</truckid>" << std::endl;
      r << "</header>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      name.clear();
      description.clear();
      departure_time = gim::time(0,0);
      MissionLocation.Clear();
      truck_id = 0;
    }

    std::string name;
    std::string description;
    gim::time departure_time;
    SMissionLocation MissionLocation;
    unsigned int truck_id;
    
  };


  /** Task type for getting an empty pallet for pick mission.
   *
   * This tag contains information about fetching an empty pallet to
   * be filled by pick missions.
   */
  struct SGetEmpty : public SBaseElement
  {
    SGetEmpty(const SNodeHeader &aNodeHeader = SNodeHeader(),
              const SMissionLocation &aMissionLocation = SMissionLocation())
      : nodeheader(aNodeHeader),
        MissionLocation(aMissionLocation)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
      
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<get_empty " << nodeheader.ToXMLString() << ">" << std::endl;
      r << MissionLocation.ToXMLString();
      r << "</get_empty>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      nodeheader.Clear();
      MissionLocation.Clear();
    }

    SNodeHeader nodeheader;
    SMissionLocation MissionLocation;
  };

  
  /** Task type for transporting completed pallets to certain
   * MissionLocation.
   *
   * This tag contains information about tranport task. Transport task
   * consists of array of MissionLocations. Each MissionLocation may or may have
   * trigger input from another task or MissionLocation.
   */
  struct STransport : public SBaseElement
  {
    STransport(const SNodeHeader &aNodeHeader = SNodeHeader(),
               const TMissionLocationArray &aMissionLocationArray = TMissionLocationArray())
      : nodeheader(aNodeHeader),
        MissionLocations(aMissionLocationArray)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<transport " << nodeheader.ToXMLString() << ">" << std::endl;
      for(EACH_IN_i(MissionLocations)) {
        const SMissionLocation &MissionLocation = *i;
        r << MissionLocation.ToXMLString();
      }
      r << "</transport>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      nodeheader.Clear();
      MissionLocations.clear();
    }


    void AddMissionLocation(const SMissionLocation &aMissionLocation) {
      MissionLocations.push_back(aMissionLocation);
    }

    SNodeHeader nodeheader;
    TMissionLocationArray MissionLocations;
  };


  /** Task type for picking a certain item in a pallet.
   *
   * Pick tasks are part of a pallet and a subgroup of 'Fill'. Each
   * pick means a pickup of single item (times 'quantity').
   */
  struct SPick : public SBaseElement
  {
    SPick(const SNodeHeader &aNodeHeader = SNodeHeader(),
          const std::string &aItemName = "",
          const unsigned int &aItemId = 0,
          const std::string &aQuantity = "",
          const TMissionLocationArray &aMissionLocations = TMissionLocationArray())
      : nodeheader(aNodeHeader),
        item_name(aItemName),
        item_id(aItemId),
        quantity(aQuantity),
        MissionLocations(aMissionLocations)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<pick " << nodeheader.ToXMLString() << ">" << std::endl;
      r << "<item_name>" << item_name << "</item_name>" << std::endl;
      r << "<item_id>" << item_id << "</item_id>" << std::endl;
      r << "<quantity>" << quantity << "</quantity>" << std::endl;

      for(EACH_IN_i(MissionLocations)) {
        const SMissionLocation &MissionLocation = *i;
        r << MissionLocation.ToXMLString();
      }
 
      r << "</pick>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      nodeheader.Clear();
      item_name.clear();
      item_id = 0;
      quantity.clear();
      MissionLocations.clear();
    }
    
    void AddMissionLocation(const SMissionLocation &aMissionLocation) {
      MissionLocations.push_back(aMissionLocation);
    }

    SNodeHeader nodeheader;
    std::string item_name;
    unsigned int item_id;
    std::string quantity;
    TMissionLocationArray MissionLocations;
  };
  typedef std::list<SPick> TPickArray;


  /** Fill is a supergroup of multiple Picks.
   *
   * Fill supergroup contains an group of SPick subtasks.
   */
  struct SFill : public SBaseElement
  {
    SFill(const SNodeHeader &aNodeHeader = SNodeHeader(),
          const TPickArray &aPickArray = TPickArray())
      : nodeheader(aNodeHeader),
        picks(aPickArray)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<fill " << nodeheader.ToXMLString() << ">" << std::endl;

      for(EACH_IN_i(picks)) {
        const SPick &pick = *i;
        r << pick.ToXMLString();
      }
 
      r << "</fill>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      nodeheader.Clear();
      picks.clear();
    }

    void AddPick(const SPick &aPick) {
      picks.push_back(aPick);
    }

    SNodeHeader nodeheader;
    TPickArray picks;

  };


  /** Pallet is a container for operations related to single pallet.
   *
   * This container contains information about single pallet. Pallet
   * may contain three different elements; 'Get_Empty', 'Fill', and
   * 'Transport'. Get_Empty element contains information about
   * fetching an mpty pallet. Fill contains one or more elements to
   * pick and store to the pallet and transport contains information
   * about the final destination of the pallet.
   *
   */
  struct SPallet : public SBaseElement
  {
    SPallet(const SNodeHeader &aNodeHeader = SNodeHeader(),
            const SGetEmpty &aGetEmpty = SGetEmpty(),
            const SFill &aFill = SFill(),
            const STransport &aTransport = STransport() )
      : nodeheader(aNodeHeader),
        get_empty(aGetEmpty),
        fill(aFill),
        transport(aTransport)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<pallet " << nodeheader.ToXMLString() << ">" << std::endl;
      r << get_empty.ToXMLString();
      r << fill.ToXMLString();
      r << transport.ToXMLString();
      r << "</pallet>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      nodeheader.Clear();
      get_empty.Clear();
      fill.Clear();
      transport.Clear();
    }
    
    SNodeHeader nodeheader;
    SGetEmpty get_empty;
    SFill fill;
    STransport transport;
  };


  /** Supergroup for multiple pallet elements.
   *
   * Supergroup containing an array of SPallet elements.
   */
  struct SPallets : public SBaseElement
  {
    SPallets()
      : pallets()
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<pallets>" << std::endl;
      
      for(EACH_IN_i(pallets)) {
        const SPallet &pallet = *i;
        r << pallet.ToXMLString();
      }
      
      r << "</pallets>" << std::endl;
      return r.str();
    }
    void Clear(void) {
      pallets.clear();
    }
    
    void AddPallet(const SPallet &aPallet) {
      pallets.push_back(aPallet);
    }


    std::list<SPallet> pallets;
  };
  typedef std::list<SPallet> TPalletArray;


  /** Mission container.
   *
   * This container contains the whole mission as structure. It
   * follows the structure of the XML format, hence making it easy to
   * use by using the XML file as reference.
   *
   */
  struct SMission : public SBaseElement
  {
    SMission(const SNodeHeader &aNodeHeader = SNodeHeader(),
             const SMissionHeader &aMissionHeader = SMissionHeader(),
             const SPallets &aPallets = SPallets())
      : nodeheader(aNodeHeader),
        header(aMissionHeader),
        pallets(aPallets)
    {}
    void Print(const unsigned int &aLevel, const std::string &aPrefix="") {
    }
    std::string ToXMLString(void) const {
      std::stringstream r;
      r << "<mission " << nodeheader.ToXMLString() << ">" << std::endl;
      r << header.ToXMLString();
      r << pallets.ToXMLString();
      r << "</mission>" << std::endl;
       return r.str();
    }
    void Clear(void) {
      nodeheader.Clear();
      header.Clear();
      pallets.Clear();
    }

    SNodeHeader nodeheader;
    SMissionHeader header;
    SPallets pallets;
  };

}

#endif
